package com.zy.util;

import org.apache.commons.lang3.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author zhy
 * @date 2025/6/20 10:50
 */
public class AesUtil {
    /**
     * 密钥算法
     */
    private static final String ALGORITHM = "AES";
    /**
     * 加密解密算法
     * <p>
     * GCM 模式必须显式指定加密参数（IV 向量和认证标签长度）
     * <p>
     * IV 向量：推荐 12 字节（96 位），每次加密应使用随机值
     * 认证标签长度：常见 16 字节（128 位），单位是位（需 ×8）
     */
    private static final String TRANSFORMATION = "AES/GCM/NoPadding";
    /**
     * 认证标签长度（位）
     */
    private static final int GCM_TAG_LENGTH = 128;
    /**
     * 初始化向量长度（字节）
     */
    private static final int GCM_IV_LENGTH = 12;
    private static final String CONST_KEY = "kmCLh9mxdfdEuXRLjp3hkw==";
    private static final String ENCRY_REPLACE = "ENCRY(%s)";
    private static final String ENCRY_PATTERN = "ENCRY\\((.*)\\)";
    private static final Pattern pattern = Pattern.compile(ENCRY_PATTERN);

    /**
     * 生成随机 AES 密钥
     */
    public static String generateKey(int... keySize) throws Exception {
        KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM);
        int len = keySize.length == 0 ? GCM_TAG_LENGTH : keySize[0];
        keyGen.init(len, SecureRandom.getInstanceStrong());
        SecretKey secretKey = keyGen.generateKey();
        return Base64.getEncoder().encodeToString(secretKey.getEncoded());
    }

    /**
     * AES-GCM 加密
     */
    public static String encrypt(String plaintext) throws Exception {
        return encrypt(plaintext, CONST_KEY);
    }

    /**
     * AES-GCM 解密
     */
    public static String decrypt(String ciphertext) throws Exception {
        return decrypt(ciphertext, CONST_KEY);
    }

    /**
     * AES-GCM 加密
     */
    public static String encrypt(String plaintext, String base64Key) throws Exception {
        if (StringUtils.isEmpty(plaintext)) {
            return "";
        }
        // 如果包含加密标识，则为加密数据，直接返回
        // 如果不包含加密标识，则为非加密数据，需要加密
        Matcher matcher = pattern.matcher(plaintext);
        if (matcher.find()) {
            return plaintext;
        }
        // 解析密钥
        byte[] keyBytes = Base64.getDecoder().decode(base64Key);
        SecretKeySpec secretKey = new SecretKeySpec(keyBytes, ALGORITHM);

        // 生成随机 IV
        SecureRandom secureRandom = SecureRandom.getInstanceStrong();
        byte[] iv = new byte[GCM_IV_LENGTH];
        secureRandom.nextBytes(iv);

        // 初始化加密器
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);

        // 执行加密
        byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));

        // 合并 IV 和密文
        byte[] encryptedBytes = new byte[iv.length + ciphertext.length];
        System.arraycopy(iv, 0, encryptedBytes, 0, iv.length);
        System.arraycopy(ciphertext, 0, encryptedBytes, iv.length, ciphertext.length);

        String encodeToString = Base64.getEncoder().encodeToString(encryptedBytes);
        return String.format(ENCRY_REPLACE, encodeToString);
    }

    /**
     * AES-GCM 解密
     */
    public static String decrypt(String ciphertext, String base64Key) throws Exception {
        if (StringUtils.isEmpty(ciphertext)) {
            return "";
        }
        // 如果有前缀，则为加密数据，需要解密
        // 如果没有前缀，则为非加密数据，无需解密直接返回。
        Matcher matcher = pattern.matcher(ciphertext);
        if (!matcher.find()) {
            return ciphertext;
        }
        if(matcher.groupCount()<1){
            return ciphertext;
        }
        ciphertext = matcher.group(1);
        // 解析输入
        byte[] encryptedBytes = Base64.getDecoder().decode(ciphertext);
        byte[] keyBytes = Base64.getDecoder().decode(base64Key);

        // 分离 IV 和密文
        byte[] iv = Arrays.copyOfRange(encryptedBytes, 0, GCM_IV_LENGTH);
        byte[] cipherBytes = Arrays.copyOfRange(encryptedBytes, GCM_IV_LENGTH, encryptedBytes.length);

        // 初始化解密器
        SecretKeySpec secretKey = new SecretKeySpec(keyBytes, ALGORITHM);
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);

        // 执行解密
        byte[] decryptedBytes = cipher.doFinal(cipherBytes);
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }
}
